package graphicView;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import model.Puzzle;

/**
 * GraphicUI is the swing implementation of the user interface.
 * It manages the menu, puzzle picture, text fields, and button.
 * It listens for various events.
 * @author David Hutchens and Beth Katz  
 */

public class GraphicUI extends JPanel
		implements MouseListener, ActionListener {
	
	private static final long serialVersionUID = 1L;
	private JFrame frame;
	private GraphicController controller;
	private JTextField inputField;
	private JTextArea theOutputMessage;

	private static int LetterSize = 20;
	private static int borderSize = 15;
	private static int MaxDrawingWidth = 10000;
	private static int MinDrawingWidth = 300;
	
	private static final String saveMenuText = "Save Puzzle";
	private static final String openMenuText = "Open Puzzle";
	private static final String findButtonText = "Find Word";
	private static final String remainingText = "Print Remaining Letters";
	private static final String resetText = "Reset Used Letters";
	
	/**
	 * Constructor for GraphicUI uses the single Display object
	 * which has a single Shell (window) inside (it could have more),
	 * and puts some things inside the shell
	 */
	public GraphicUI(GraphicController aController, JFrame aFrame) {
		frame = aFrame;
		controller = aController;
	   	JPanel widgetPanel = setupLayout();
	   	frame.getContentPane().add(widgetPanel, BorderLayout.CENTER);
		createMenu(frame);
		createTextItems(widgetPanel);
		createButton(widgetPanel);
		setSizes();
		// addMouseListener(this);
	}
	
	/**
	 * returns the current puzzle
	 * @return the current puzzle
	 */
	public Puzzle puzzle() {
		return controller.puzzle();
	}
	
	/**
	 * Sets the sizes based on the current puzzle.
	 */
	public void setSizes() {
		Puzzle thePuzzle = puzzle();
		Dimension puzzleDrawingSize = new Dimension (thePuzzle.numberOfColumns()*LetterSize + borderSize*2,
													 thePuzzle.numberOfRows()*LetterSize + borderSize*2);
		setMinimumSize(puzzleDrawingSize);
	   	setPreferredSize(puzzleDrawingSize);
	   	setMaximumSize(puzzleDrawingSize);
	   	revalidate();
	}
	
	/**
	 * Paints a graphical version of the puzzle.
	 */
	public void printPuzzle( ) {
		repaint();
	}
	
	/**
	 * Draws the painted portions when requested.
	 * @param Graphics gc the graphics context in which to draw 
	 */
	public void paintComponent(Graphics gc) { 
		Puzzle thePuzzle = puzzle();
		if (isOpaque()) { //paint background
            gc.setColor(getBackground());
            gc.fillRect(0, 0, getWidth(), getHeight());
		}
		gc.setColor(new Color(87, 63, 207)); 	
		gc.setFont(new Font("Times", Font.BOLD, 18));
		gc.translate(0,10);
		
		int theRows = thePuzzle.numberOfRows( );
		int theCols = thePuzzle.numberOfColumns( );
		for (int i = 1; i <= theRows; i++) {
			for (int j = 1; j <= theCols; j++) {
				char ch = thePuzzle.charAt(i,j);
				if (thePuzzle.charUsedAt(i,j)) {
					ch = Character.toUpperCase(ch);
				}
				String s = String.valueOf(ch);
				gc.drawString(s, LetterSize*j, LetterSize*i);
			}
		}
		
		gc.translate(0,-10);
	} 
	
	/**
	 * Set up the layout for the interface
	 * @return returns the JPanel that will hold the widgets and this UI
	 */
	private JPanel setupLayout() {
		JPanel widgetPanel = new JPanel();
		widgetPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
		widgetPanel.setLayout(new BoxLayout(widgetPanel, BoxLayout.PAGE_AXIS));
		widgetPanel.add(this);
	   	
	   	Dimension puzzleDrawingSize = new Dimension(LetterSize, LetterSize);
	   	setMinimumSize(puzzleDrawingSize);
	   	setPreferredSize(puzzleDrawingSize);
	   	setMaximumSize(puzzleDrawingSize);
	   	setBorder(BorderFactory.createLineBorder(Color.lightGray, 2));
	   	return widgetPanel;
	}
	
	/**
	 * Creates all parts of the menu for the application.
	 * @param frame the enclosing frame for the interface
	 */
	private void createMenu(JFrame frame) {
		// create a menu bar
		JMenuBar menuBar = new JMenuBar();
		frame.setJMenuBar(menuBar);
				
		// create the File Item on the menu bar
		JMenu fileMenu = new JMenu("File"); 
		menuBar.add(fileMenu);
		
		// create the openPuzzle submenu item and listen for it
		JMenuItem openPuzzle = new JMenuItem(openMenuText); 
		openPuzzle.addActionListener(this);
		fileMenu.add(openPuzzle);
		
		// create the savePuzzle submenu item and listen for it
		JMenuItem savePuzzle = new JMenuItem(saveMenuText); 
		savePuzzle.addActionListener(this);
		fileMenu.add(savePuzzle);
		
		// create the Action Item on the menu bar
		JMenu actionMenu = new JMenu("Action"); 
		menuBar.add(actionMenu);

		// create the find word submenu item and listen for it
		JMenuItem findWord = new JMenuItem(findButtonText); 
		findWord.addActionListener(this);
		actionMenu.add(findWord);
		
		// create the print remaining letters submenu item and listen for it
		JMenuItem printRemaining = new JMenuItem(remainingText); 
		printRemaining.addActionListener(this);
		actionMenu.add(printRemaining);
		
		// create the print reset used submenu item and listen for it
		JMenuItem resetUsedLetters = new JMenuItem(resetText); 
		resetUsedLetters.addActionListener(this);
		actionMenu.add(resetUsedLetters);
	}
	
	/**
	 * Creates the text items including places to enter text and labels.
	 * Labels cannot be changed by the user.
	 * Text can be changed by the user unless it's not editable.
	 * @param widgetPanel is the JPanel that will hold the text items.
	 */
	private void createTextItems(JPanel widgetPanel) {
		widgetPanel.add(Box.createRigidArea(new Dimension(0,5)));
		
		JPanel panel = new JPanel();
		panel.setAlignmentX(CENTER_ALIGNMENT);
		panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
		widgetPanel.add(panel);
		
		JLabel inputLabel = new JLabel();
		panel.add(inputLabel);
		inputLabel.setText("Enter a word to find:");
		inputLabel.setAlignmentX(RIGHT_ALIGNMENT);
		
		inputField = new JTextField();
		panel.add(inputField);
		inputField.setText("");
		inputField.setEditable(true);
		inputField.setMaximumSize(new Dimension(MaxDrawingWidth,30));
		inputField.addActionListener(this);
		
		widgetPanel.add(Box.createRigidArea(new Dimension(0,5)));

		theOutputMessage = new JTextArea();
		JScrollPane scrollPane = new JScrollPane(theOutputMessage);
		widgetPanel.add(scrollPane);
		theOutputMessage.setEditable(false);
		theOutputMessage.setText("Open a Puzzle to Start");
		scrollPane.setMinimumSize(new Dimension(MinDrawingWidth,30));
		scrollPane.setPreferredSize(new Dimension(MinDrawingWidth,100));
	}
	
	/**
	 * Creates the button to look up a word.
	 * @param widgetPanel is the JPanel that will hold the button.
	 */
	private void createButton(JPanel widgetPanel) {
		JButton findButton = new JButton(findButtonText);
		findButton.addActionListener(this);
		widgetPanel.add(findButton);
		findButton.setAlignmentX(CENTER_ALIGNMENT);

		JButton remainingButton = new JButton(remainingText);
		remainingButton.addActionListener(this);
		widgetPanel.add(remainingButton);
		remainingButton.setAlignmentX(CENTER_ALIGNMENT);
	}
	
	/**
	 * Prints the message in the output field.
	 * @param theMessage message to be printed
	 */
	public void printMessage(String theMessage) {
		theOutputMessage.setText(theMessage);
	}
	
	/**
	 * Returns the word currently in the input field.
	 * @return current value of the input field
	 */
	public String getWord() {
		return inputField.getText();
	}
	
	/**
	 * Handles the mouse entered event by printing a message.
	 * @param me the mouse event containing location and modifier keys
	 */
	public void mouseEntered(MouseEvent e) {
		printMessage("Mouse entered");
	}

	/**
	 * Handles the mouse exited event by printing a message.
	 * @param me the mouse event containing location and modifier keys
	 */
	 public void mouseExited(MouseEvent e) {
		 printMessage("Mouse exited");
	 }

	/**
	 * Handles the mouse click event by printing the number of clicks.
	 * @param me the mouse event containing location and modifier keys
	 */
	public void mouseClicked(MouseEvent me) {
		printMessage("Mouse clicked (# of clicks: " + me.getClickCount() + ")");	
	}
	
	/**
	 * Handles the mouse down event by printing a message..
	 * @param me the mouse event containing location and modifier keys
	 */
	public void mousePressed(MouseEvent me) {
		printMessage("MouseDown")	;
	}
	
	/**
	 * Handles the mouse up event by printing a message.
     * @param me the mouse event containing location and modifier keys
     */
	public void mouseReleased(MouseEvent me) {
		printMessage("MouseUp");	
	}
	
	/**
	 * Perform actions depending which widget was selected.
	 * Determines which sort of widget was selected and bases action on its name.
	 * Currently has actions for the find word button and the open puzzle menu.
	 * @param se the selection event including the source of the event
	 */
	public void actionPerformed(ActionEvent se) {
		if (se.getSource() == inputField) {
			findWord();
		} else {
			String command = se.getActionCommand();
			if (command == findButtonText) {
				findWord( );
			} else if (command == remainingText) {
				printRemaining( );
			} else if (command == resetText) {
				resetToUnused( );
			} else if (command == openMenuText) {
				controller.openPuzzleFile( );
			} else if (command == saveMenuText) {
				printMessage("That was a save puzzle request");
			} else {
				printMessage("Unknown action: " + command);
			}
		}
	}
	
	/**
	 * Look up the current word in the puzzle and print the result.
	 */
	private void findWord( ) {
		String theWord = getWord();
		if (theWord == null || theWord.length() <= 0) {
			printMessage("No word to find");
			return;
		}
		String theAnswer = puzzle().locationInPuzzle(theWord);
		if (theAnswer.length( ) <= 0) {
			printMessage(theWord + " not found");
		} else {
			printMessage(theAnswer);		
			printPuzzle( );
		}
		inputField.selectAll(); // prepares the inputField to replace with next keystrokes
		inputField.requestFocusInWindow(); // makes the input field receive keystrokes
	}
	
	/**
	 * Print the unused letters remaining in the puzzle.
	 */
	private void printRemaining( ) {
		Puzzle thePuzzle = puzzle();
		printMessage("Remaining letters: " + thePuzzle.stringOfRemainingLetters( ));
	}
	
	/**
	 * Reset the letters in the puzzle to unused.
	 */
	private void resetToUnused( ) {
		Puzzle thePuzzle = puzzle();
		thePuzzle.resetUsed( );
		printPuzzle( );
	}
	
	/**
	 * Opens a file dialog using the provided message and 
	 * obtains the full name of a file.
	 * @param message the prompt in the file dialog box
	 * @return full path name of the file name
	 */
	public String obtainFileName(String message) {
		String theFileName = null;
		final JFileChooser fc = new JFileChooser();
		int returnVal = fc.showOpenDialog(this);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
	           File file = fc.getSelectedFile();
	           try {
	        	   theFileName = file.getCanonicalPath();
	           } catch (Exception e) {
	        	   printMessage("Unable to determine file path.");
	        	   theFileName = null;
	           }
		}
  		return (theFileName);
	}
	
	
}
